﻿using System;
using System.Linq;
using System.Text;
using AchSegítő.extensions;

namespace AchSegítő.File
{
    /// <summary>
    /// Grand total of transactions and amounts
    /// </summary>
    public class FileControl
    {
        #region Private

        #endregion
        /// <summary>
        /// THIS IS THE FIRST POSITION FOR ALL RECORD FORMATS. THE NUMBER
        /// IS UNIQUE FOR EACH RECORD TYPE. THE FILE CONTROL RECORD USES
        /// RECORD TYPE CODE 9.
        /// </summary>
        public readonly RecordTypeCodes RecordType = RecordTypeCodes.FileControl;

        /// <summary>
        /// VALUE MUST BE EQUAL TO THE NUMBER OF ‘8’ BATCH RECORDS IN FILE.
        /// </summary>
        public int BatchCount { get; set; } = 0;

        /// <summary>
        /// NUMBER OF PHYSICAL BLOCKS IN THE FILE, INCLUDING FILE HEADER AND FILE CONTROL RECORDS.
        /// </summary>
        public int BlockCount { get; set; } = 0;

        /// <summary>
        /// SUM OF ALL ‘6’ RECORDS AND ALSO '7' RECORDS, IF USED.
        /// </summary>
        public int EntryCount { get; set; } = 0;

        /// <summary>
        /// SUM OF ALL RECEIVING DEPOSITORY FINANCIAL INSTITUTION IDS IN EACH ‘6’ RECORD.
        /// IF SUM IS MORE THAN 10 POSITIONS, TRUNCATE LEFTMOST NUMBERS.
        /// </summary> 
        public String EntryHash { get; set; } = String.Empty;

        /// <summary>
        /// TOTAL OF ALL DEBIT AMOUNTS IN ‘8’ RECORDS, POSITIONS 21-32.
        /// </summary>
        public Decimal TotalDebits { get; set; } = decimal.Zero;

        /// <summary>
        /// TOTAL OF ALL CREDIT AMOUNTS IN ‘8’ RECORDS, POSITIONS 33-44.
        /// </summary>
        public Decimal TotalCredits { get; set; } = decimal.Zero;

        public readonly String Reserved = "      ";
        public FileControl()
        {
            // Nothing to do here
        }
        internal FileControl(ACH file)
        {
            BatchCount = file.Batches.Count;

            EntryCount = file.Batches.Sum(batch => batch.Entries.Count);

            TotalCredits = file.Batches.Sum(batch => batch.Control.TotalCredits);
            TotalDebits = file.Batches.Sum(batch => batch.Control.TotalDebits);

            BlockCount = (int)Math.Ceiling(ACH.GetNumberOfLines(file) / 10m);

            EntryHash = CalculateEntryHash(file);
        }

        public override string ToString()
        {
            var sb = new StringBuilder(ACH.CHARACTERS_PER_LINE);

            sb.Append((int)RecordType);

            sb.Append(BatchCount.ToString().TrimAndPadLeft(6, '0'));

            sb.Append(BlockCount.ToString().TrimAndPadLeft(6, '0'));

            sb.Append(EntryCount.ToString().TrimAndPadLeft(8, '0'));

            sb.Append(EntryHash.TrimAndPadLeft(10, '0'));

            sb.Append((TotalDebits * 100).ToString("0").TrimAndPadLeft(12, '0')); // 123.45 => 000000012345

            sb.Append((TotalCredits * 100).ToString("0").TrimAndPadLeft(12, '0')); // 123.45 => 000000012345

            sb.Append(Reserved.TrimAndPadRight(39));

            sb.AppendLine();

            return sb.ToString();
        }

        public static String CalculateEntryHash(ACH file)
        {
            var sum =
                file
                .Batches
                .SelectMany(batch => batch.Entries)
                .Select(entry => entry.ReceivingDFIIdentification)
                .Select(routingNumber => routingNumber.Substring(0, 8))
                .Select(substring => Decimal.Parse(substring))
                .Sum();

            //IF THE SUM OF THE RDFI TRANSIT ROUTING NUMBERS IS A
            //NUMBER GREATER THAN TEN DIGITS, REMOVE OR DROP THE
            //NUMBER OF DIGITS FROM THE LEFT SIDE OF THE NUMBER
            //UNTIL ONLY TEN DIGITS REMAIN. FOR EXAMPLE, IF THE SUM
            //OF THE TRANSIT ROUTING NUMBERS IS 234567898765,
            //REMOVE THE “23” FOR A HASH OF 4567898765.

            var last10 = sum % 10000000000;

            return last10.ToString();
        }

    }
}